#include "../include/command.h"

byte codes[0x10000];
byte data[0x10000];
byte stack[0x10000];
byte buffer[0x10000];

extern qword iregs[128];
extern real4 fregs[64];
extern real8 dregs[64];

Command loader()
{
	Command result = {};
	result.opcode = *(codes + iregs[Code]);
	result.treg = *(codes + iregs[Code] + 1);
	result.sreg1 = *(codes + iregs[Code] + 2);
	result.sreg2 = *(codes + iregs[Code] + 3);
	result.imm1 = *(codes + iregs[Code] + 4);
	result.imm2 = *(codes + iregs[Code] + 5);
	result.imm3 = *(codes + iregs[Code] + 6);
	result.imm3 = (result.imm3 << 8) + *(codes + iregs[Code] + 7);
	iregs[Code] += 8;
	return result;
}
void selector(Command opcode)
{
	switch (opcode.opcode >> 4)
	{
	case SPEC:
		special(opcode);
		break;
	case CALC:
		calculator(opcode);
		break;
	case MEMOP:
		memop(opcode);
		break;
	}
}
void special(Command opcode)
{
	char *temp;
	switch (opcode.opcode & 0x0F)
	{
	case 0x00: // nop
		break;
	case 0x01: // push command
		if (iregs[Flag] & STACK_FULL)
		{
			throw_error({
				0x10,
				"Stack is full!",
				(word)((iregs[Code] - 8) & 0xffff),
			});
			exit(2);
		}
		if (opcode.treg < 128)
		{
			memcpy(stack + iregs[Stack], iregs + (opcode.treg & 0x7f), 8);
			iregs[Stack] += 8;
		}
		else if (opcode.treg < 192)
		{
			memcpy(stack + iregs[Stack], fregs + (opcode.treg & 0x3f), 4);
			iregs[Stack] += 4;
		}
		else
		{
			memcpy(stack + iregs[Stack], dregs + (opcode.treg & 0x3f), 8);
			iregs[Stack] += 8;
		}
		if (iregs[Stack] > 0xfff8)
			iregs[Flag] |= STACK_FULL;
		break;
	case 0x02: // pop command
		if (iregs[Flag] & STACK_EMPTY)
		{

			throw_error({
				0x10,
				"Stack is empty!",
				(word)((iregs[Code] - 8) & 0xffff),
			});
			exit(2);
		}
		if (opcode.treg < 128)
		{
			memcpy(iregs + (opcode.treg & 0x7f), stack + iregs[Stack] - 8, 8);
			iregs[Stack] -= 8;
		}
		else if (opcode.treg < 192)
		{
			memcpy(fregs + (opcode.treg & 0x3f), stack + iregs[Stack] - 4, 4);
			iregs[Stack] -= 4;
		}
		else
		{
			memcpy(dregs + (opcode.treg & 0x3f), stack + iregs[Stack] - 8, 8);
			iregs[Stack] -= 8;
		}
		if (iregs[Stack] < 0x08)
			iregs[Flag] &= STACK_EMPTY;
		break;
	case 0x03: // mov
		switch (opcode.imm1)
		{
		case BYTE:
			iregs[opcode.treg & 0x7f] &= 0xffffffffffffff00ULL;
			iregs[opcode.treg & 0x7f] |= iregs[opcode.sreg1 & 0x7f] & 0xff;
			break;
		case WORD:
			iregs[opcode.treg & 0x7f] &= 0xffffffffffff0000ULL;
			iregs[opcode.treg & 0x7f] |= iregs[opcode.sreg1 & 0x7f] & 0xffff;
			break;
		case DWORD:
			iregs[opcode.treg & 0x7f] &= 0xffffffff00000000ULL;
			iregs[opcode.treg & 0x7f] |= iregs[opcode.sreg1 & 0x7f] & 0xffffffff;
			break;
		case QWORD:
			iregs[opcode.treg & 0x7f] = iregs[opcode.sreg1 & 0x7f];
			break;
		case REAL4:
			fregs[opcode.treg & 0x3f] = fregs[opcode.sreg1 & 0x3f];
			break;
		case REAL8:
			dregs[opcode.treg & 0x3f] = dregs[opcode.sreg1 & 0x3f];
			break;
		}
		break;
	case 0x04: // cast
		switch (opcode.imm1)
		{
			/*
			 * imm1:cast type
			 * 0:int,1:float,2:double
			 */
		case 0x00:
		case 0x11:
		case 0x22:
			// No type cast,pass
			break;
		case 0x01:
			fregs[opcode.treg & 0x3f] = (float)iregs[opcode.sreg1 & 0x7f];
			break;
		case 0x02:
			dregs[opcode.treg & 0x3f] = (double)iregs[opcode.sreg1 & 0x7f];
			break;
		case 0x10:
			iregs[opcode.treg & 0x7f] = (qword)fregs[opcode.sreg1 & 0x3f];
			break;
		case 0x12:
			dregs[opcode.treg & 0x3f] = (double)fregs[opcode.sreg1 & 0x3f];
			break;
		case 0x20:
			iregs[opcode.treg & 0x7f] = (qword)dregs[opcode.sreg1 & 0x3f];
			break;
		case 0x21:
			fregs[opcode.treg & 0x3f] = (float)dregs[opcode.sreg1 & 0x3f];
			break;
		}
	case 0x05: // sbit
		iregs[Flag] |= (qword)1 << (opcode.imm1 & 0x3f);
		break;
	case 0x06: // cbit
		iregs[Flag] &= ~((qword)1 << (opcode.imm1 & 0x3f));
		break;
	case 0x08: // ret
		if (iregs[Flag] & STACK_EMPTY)
		{

			throw_error({
				0x10,
				"Stack is empty!",
				(word)((iregs[Code] - 8) & 0xffff),
			});
			exit(2);
		}
		memcpy(iregs + Code, stack + iregs[Base] - 8, 2);
		memcpy(iregs + Base, stack + iregs[Base] - 4, 2);
		break;
	case 0x0A: // input
		for (byte i = 0; i < opcode.imm1; i++)
			scanf("%c", buffer + (opcode.imm3 + i));
		break;
	case 0x0B: // output
		for (byte i = 0; i < opcode.imm1; i++)
			printf("%c", buffer[opcode.imm3 + i]);
		break;
	case 0x0c:
	{
		memcpy(temp, (char *)buffer + (iregs[opcode.sreg1 & 0x7f] & 0xffff), opcode.imm1);
		FILE *in = fopen(temp, "rb");
		fseek(in, (long)(iregs[opcode.sreg1 & 0x7f] >> 16), 0);
		fread(buffer + (iregs[opcode.treg & 0x7f] & 0xffff), sizeof(byte), opcode.imm3, in);
	}
	break;
	case 0x0d:
	{
		memcpy(temp, (char *)buffer + (iregs[opcode.sreg1 & 0x7f] & 0xffff), opcode.imm1);
		FILE *out = fopen(temp, "wb");
		fseek(out, (long)(iregs[opcode.sreg1 & 0x7f] >> 16), 0);
		fwrite(buffer + (iregs[opcode.treg & 0x7f] & 0xffff), sizeof(byte), opcode.imm3, out);
	}
	break;
	}
}
void calculator(Command opcode)
{
	switch (opcode.opcode & 0x0F)
	{
	case 0x00:
		switch (opcode.imm1)
		{
		case 0x00:
			iregs[opcode.treg & 0x7f] = iregs[opcode.sreg1 & 0x7f] + iregs[opcode.sreg2 & 0x7f];
			break;
		case 0x01:
			iregs[opcode.treg & 0x7f] = iregs[opcode.sreg1 & 0x7f] - iregs[opcode.sreg2 & 0x7f];
			break;
		case 0x02:
			iregs[opcode.treg & 0x7f] = iregs[opcode.sreg1 & 0x7f] * iregs[opcode.sreg2 & 0x7f];
			break;
		case 0x03:
			iregs[opcode.treg & 0x7f] = iregs[opcode.sreg1 & 0x7f] / iregs[opcode.sreg2 & 0x7f];
			break;
		case 0x04:
			iregs[opcode.treg & 0x7f] = iregs[opcode.sreg1 & 0x7f] % iregs[opcode.sreg2 & 0x7f];
			break;
		}
		break;
	case 0x01:
		switch (opcode.imm1)
		{
		case 0x00:
			fregs[opcode.treg & 0x3f] = fregs[opcode.sreg1 & 0x3f] + fregs[opcode.sreg2 & 0x3f];
			break;
		case 0x01:
			fregs[opcode.treg & 0x3f] = fregs[opcode.sreg1 & 0x3f] - fregs[opcode.sreg2 & 0x3f];
			break;
		case 0x02:
			fregs[opcode.treg & 0x3f] = fregs[opcode.sreg1 & 0x3f] * fregs[opcode.sreg2 & 0x3f];
			break;
		case 0x03:
			fregs[opcode.treg & 0x3f] = fregs[opcode.sreg1 & 0x3f] / fregs[opcode.sreg2 & 0x3f];
			break;
		}
		break;
	case 0x02:
		switch (opcode.imm1)
		{
		case 0x00:
			dregs[opcode.treg & 0x3f] = dregs[opcode.sreg1 & 0x3f] + dregs[opcode.sreg2 & 0x3f];
			break;
		case 0x01:
			dregs[opcode.treg & 0x3f] = dregs[opcode.sreg1 & 0x3f] - dregs[opcode.sreg2 & 0x3f];
			break;
		case 0x02:
			dregs[opcode.treg & 0x3f] = dregs[opcode.sreg1 & 0x3f] * dregs[opcode.sreg2 & 0x3f];
			break;
		case 0x03:
			dregs[opcode.treg & 0x3f] = dregs[opcode.sreg1 & 0x3f] / dregs[opcode.sreg2 & 0x3f];
			break;
		}
		break;
	case 0x08:
		iregs[opcode.treg & 0x7f] = iregs[opcode.sreg1 & 0x7f] & iregs[opcode.sreg2 & 0x7f];
		break;
	case 0x09:
		iregs[opcode.treg & 0x7f] = iregs[opcode.sreg1 & 0x7f] | iregs[opcode.sreg2 & 0x7f];
		break;
	case 0x0A:
		iregs[opcode.treg & 0x7f] = iregs[opcode.sreg1 & 0x7f] ^ iregs[opcode.sreg2 & 0x7f];
		break;
	case 0x0B:
		iregs[opcode.treg & 0x7f] = ~iregs[opcode.sreg1 & 0x7f];
		break;
	case 0x0C:
		iregs[opcode.treg & 0x7f] = iregs[opcode.sreg1 & 0x7f] << (opcode.imm1 & 0x3f);
		break;
	case 0x0D:
		iregs[opcode.treg & 0x7f] = iregs[opcode.sreg1 & 0x7f] >> (opcode.imm1 & 0x3f);
		break;
	}
}
void memop(Command opcode)
{
	switch (opcode.opcode & 0x0f)
	{
	case 0x00: // load
		switch (opcode.imm2)
		{
		case BYTE:
		case WORD:
		case DWORD:
		case QWORD:
			switch (opcode.imm1)
			{
			case 0x00:
				memcpy(iregs + (opcode.treg & 0x7f), codes + opcode.imm3, (qword)1 << opcode.imm2);
				break;
			case 0x01:
				memcpy(iregs + (opcode.treg & 0x7f), data + opcode.imm3, (qword)1 << opcode.imm2);
				break;
			case 0x03:
				memcpy(iregs + (opcode.treg & 0x7f), buffer + opcode.imm3, (qword)1 << opcode.imm2);
				break;
			}
			break;
		case REAL4:
			switch (opcode.imm1)
			{
			case 0x00:
				memcpy(iregs + (opcode.treg & 0x3f), codes + opcode.imm3, sizeof(real4));
				break;
			case 0x01:
				memcpy(iregs + (opcode.treg & 0x3f), data + opcode.imm3, sizeof(real4));
				break;
			case 0x03:
				memcpy(iregs + (opcode.treg & 0x3f), buffer + opcode.imm3, sizeof(real4));
				break;
			}
			break;
		case REAL8:
			switch (opcode.imm1)
			{
			case 0x00:
				memcpy(dregs + (opcode.treg & 0x3f), codes + opcode.imm3, sizeof(real8));
				break;
			case 0x01:
				memcpy(dregs + (opcode.treg & 0x3f), data + opcode.imm3, sizeof(real8));
				break;
			case 0x03:
				memcpy(dregs + (opcode.treg & 0x3f), buffer + opcode.imm3, sizeof(real8));
				break;
			}
		}
		break;
	case 0x01: // store
		switch (opcode.imm2)
		{
		case BYTE:
		case WORD:
		case DWORD:
		case QWORD:
			switch (opcode.imm1)
			{
			case 0x00:
				memcpy(codes + opcode.imm3, iregs + (opcode.treg & 0x7f), (qword)1 << opcode.imm2);
				break;
			case 0x01:
				memcpy(data + opcode.imm3, iregs + (opcode.treg & 0x7f), (qword)1 << opcode.imm2);
				break;
			case 0x03:
				memcpy(buffer + opcode.imm3, iregs + (opcode.treg & 0x7f), (qword)1 << opcode.imm2);
				break;
			}
			break;
		case REAL4:
			switch (opcode.imm1)
			{
			case 0x00:
				memcpy(codes + opcode.imm3, iregs + (opcode.treg & 0x3f), sizeof(real4));
				break;
			case 0x01:
				memcpy(data + opcode.imm3, iregs + (opcode.treg & 0x3f), sizeof(real4));
				break;
			case 0x03:
				memcpy(buffer + opcode.imm3, iregs + (opcode.treg & 0x3f), sizeof(real4));
				break;
			}
			break;
		case REAL8:
			switch (opcode.imm1)
			{
			case 0x00:
				memcpy(codes + opcode.imm3, dregs + (opcode.treg & 0x3f), sizeof(real8));
				break;
			case 0x01:
				memcpy(data + opcode.imm3, dregs + (opcode.treg & 0x3f), sizeof(real8));
				break;
			case 0x03:
				memcpy(buffer + opcode.imm3, dregs + (opcode.treg & 0x3f), sizeof(real8));
				break;
			}
			break;
		}
		break;
	case 0x02: // jump
		switch (opcode.imm1)
		{
		case NONE:
			break;
		case EQU:
			if (iregs[opcode.sreg1 & 0x7f] == 0)
				iregs[Code] = opcode.imm3;
			break;
		case GT:
			if ((long long)iregs[opcode.sreg1 & 0x7f] > 0)
				iregs[Code] = opcode.imm3;
			break;
		case GE:
			if ((long long)iregs[opcode.sreg1 & 0x7f] >= 0)
				iregs[Code] = opcode.imm3;
			break;
		case LT:
			if ((long long)iregs[opcode.sreg1 & 0x7f] < 0)
				iregs[Code] = opcode.imm3;
			break;
		case LE:
			if ((long long)iregs[opcode.sreg1 & 0x7f] <= 0)
				iregs[Code] = opcode.imm3;
			break;
		case NEQ:
			if (iregs[opcode.sreg1 & 0x7f] != 0)
				iregs[Code] = opcode.imm3;
			break;
		case ALL:
			iregs[Code] = opcode.imm3;
			break;
		}
	case 0x03: // mem copy
		switch (opcode.imm1)
		{
		case 0x00:
			memcpy(codes + iregs[opcode.sreg1 & 0x7f], codes + iregs[opcode.sreg2 & 0x7f], opcode.imm3);
			break;
		case 0x11:
			memcpy(data + iregs[opcode.sreg1 & 0x7f], data + iregs[opcode.sreg2 & 0x7f], opcode.imm3);
			break;
		case 0x33:
			memcpy(buffer + iregs[opcode.sreg1 & 0x7f], buffer + iregs[opcode.sreg2 & 0x7f], opcode.imm3);
			break;
		default:
			throw_error({
				0x12,
				"Illegal memory copy!",
				(word)((iregs[Code] - 8) & 0xffff),
			});
			exit(2);
			break;
		}
		break;
	case 0x04: // mem cast
		switch (opcode.imm1)
		{
		case 0x00:
		case 0x11:
		case 0x33:
			break;
		case 0x01:
			memcpy(codes + iregs[opcode.treg & 0x7f], data + iregs[opcode.sreg1 & 0x7f], opcode.imm3);
			break;
		case 0x03:
			memcpy(codes + iregs[opcode.treg & 0x7f], buffer + iregs[opcode.sreg1 & 0x7f], opcode.imm3);
			break;
		case 0x10:
			memcpy(data + iregs[opcode.treg & 0x7f], codes + iregs[opcode.sreg1 & 0x7f], opcode.imm3);
			break;
		case 0x13:
			memcpy(data + iregs[opcode.treg & 0x7f], buffer + iregs[opcode.sreg1 & 0x7f], opcode.imm3);
			break;
		case 0x30:
			memcpy(buffer + iregs[opcode.treg & 0x7f], codes + iregs[opcode.sreg1 & 0x7f], opcode.imm3);
			break;
		case 0x31:
			memcpy(buffer + iregs[opcode.treg & 0x7f], data + iregs[opcode.sreg1 & 0x7f], opcode.imm3);
			break;
		}
		break;
	}
}